
-- the expected pattern of a line from UnicodeData.txt: 14 semicolon-separated fields
local fieldpattern = '^' .. string.rep('([^;]-);',14) .. '(.*)$'

local UnicodeData = io.open('UnicodeData.txt', 'r')
if not UnicodeData then
  error('please download UnicodeData.txt from http://unicode.org/versions/components-5.2.0.html and put it into the working directory')
end

local exclusions = {}

local Exclusions = io.open('CompositionExclusions.txt', 'r')
if not Exclusions then
  UnicodeData:close()
  error('please download CompositionExclusions.txt from http://unicode.org/versions/components-5.2.0.html and put it into the working directory')
end

for l in Exclusions:lines() do
  local exclusion = tonumber(l:gsub('#.*',''):gsub('%s+',''),16)
  if exclusion then
    exclusions[exclusion] = true
  end
end

local DataLibrary = assert(io.open('unicodedata_all.lua', 'w'))

DataLibrary:write('-- GENERATED SOURCE CODE FILE, PLEASE DO NOT MODIFY DIRECTLY\n')
DataLibrary:write('return {\n')

for l in UnicodeData:lines() do
  local codeValue,
        characterName,
        generalCategory,
        canonicalCombiningClasses,
        bidirectionalCategory,
        characterDecompositionMapping,
        decimalDigitValue,
        digitValue,
        numericValue,
        mirrored,
        unicode10Name,
        iso10646Comment,
        uppercaseMapping,
        lowercaseMapping,
        titlecaseMapping = string.match(l, fieldpattern)
  if codeValue then
    codeValue = tonumber(codeValue, 16)
	DataLibrary:write(string.format('  [0x%04x] = {name=%q, cat=%q', codeValue, characterName, generalCategory))
	
	if canonicalCombiningClasses ~= '0' then
	  DataLibrary:write(string.format(', combo=%d', canonicalCombiningClasses))
	end
	
	if bidirectionalCategory ~= '' then
	  DataLibrary:write(string.format(', bidi=%q', bidirectionalCategory))
	end
	
	if characterDecompositionMapping ~= '' then
	  DataLibrary:write(', decomp={')
	  characterDecompositionMapping = characterDecompositionMapping:gsub('<([^>]+)>',
	    function(tag)
		  DataLibrary:write(string.format('compat=%q;', tag))
		  return ''
		end)
	  for entry, space in characterDecompositionMapping:gmatch('([0-9a-fA-F]+)(%s*)') do
	    DataLibrary:write('0x' .. entry)
		if (#space > 0) then
		  DataLibrary:write(',')
		end
	  end
	  DataLibrary:write('}')
	end
	
	if decimalDigitValue ~= '' then
	  DataLibrary:write(', decdigit='..decimalDigitValue)
	end
	
	if digitValue ~= '' then
	  DataLibrary:write(', digit=' .. digitValue)
	end
	
	if numericValue ~= '' then
	  DataLibrary:write(', numeric=' .. numericValue)
	end
	
	if (mirrored == "Y") then
	  DataLibrary:write(', mirror=true')
	end
	
	uppercaseMapping = tonumber(uppercaseMapping, 16)
	if uppercaseMapping then
	  DataLibrary:write(string.format(', upper=0x%04x', uppercaseMapping))
	end
	lowercaseMapping = tonumber(lowercaseMapping, 16)
	if lowercaseMapping then
	  DataLibrary:write(string.format(', lower=0x%04x', lowercaseMapping))
	end
	titlecaseMapping = tonumber(titlecaseMapping, 16)
	if titlecaseMapping then
	  DataLibrary:write(string.format(', title=0x%04x', titlecaseMapping))
	end
	
	DataLibrary:write('};\n')
  end
end

DataLibrary:write('}\n')

DataLibrary:close()
UnicodeData:close()

local unicodedata = dofile("unicodedata_all.lua")

local composers = {}

for charcode, info in pairs(unicodedata) do
  local decomp = info.decomp
  if decomp and (#decomp == 2) and not decomp.compat and not exclusions[charcode] and ((info.combo or 0) == 0) then
	local k1, k2 = decomp[1], decomp[2]
	local k = composers[k1]
	if not k then
	  k = {}
	  composers[k1] = k
	end
	k[k2] = charcode
  end
end

local ComposeLibrary = io.open('unicode_compose.lua', 'w')
ComposeLibrary:write('return {\n')

for k1, k in pairs(composers) do
  ComposeLibrary:write(string.format('  [0x%04X]={\n', k1))
  for k2, charcode in pairs(k) do
    ComposeLibrary:write(string.format('    [0x%04X]=0x%04X;\n', k2, charcode))
  end
  ComposeLibrary:write('  };\n')
end

ComposeLibrary:write('}\n')
ComposeLibrary:close()
